package ap.project.controller.main;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;

import org.json.JSONException;
import org.json.JSONObject;

import ap.project.controller.service.GameSurface;

public class Connection implements Runnable {
	private Thread thread;
	private Socket socket;

	private static final Connection instance = new Connection();

	private Connection() {
	}

	public static Connection getInstance() {
		return instance;
	}

	/**
	 * Create the client connection based on the given socket
	 * 
	 * @param socket the given socket
	 */
	public void setUpConnection(Socket socket) {
		this.socket = socket;
		this.thread = new Thread(this);
		this.thread.start();
	}

	public void run() {
		BufferedReader inp = null;
		String line;
		try {
			inp = new BufferedReader(new InputStreamReader(
					socket.getInputStream()));
			// Attempt to receive data
			while ((line = inp.readLine()) != null) {
				JSONObject input = new JSONObject(line);
				handlePacket(input);
			}
		} catch (Exception e) {
			
		}
	}

	/**
	 * Send json data to game 
	 * @param jOb json object with data
	 */
	public void send(JSONObject jOb) {
		PrintWriter outp = null;

		try {
			outp = new PrintWriter(socket.getOutputStream(), true);
			if (jOb != null) {
				outp.println(jOb.toString());
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * Handle received JSON packets
	 * 
	 * @param packet received JSONObject
	 */
	private void handlePacket(JSONObject packet) {
		int code = -1;
		try {
			code = packet.getInt("Code");
		} catch (JSONException e) {
			e.printStackTrace();
		}
		switch (code) {
		case Constants.CODEHEALTH:
			handleHealth(packet);
			break;

		case Constants.CODEPOWERUP:
			handlePowerUp(packet);
			break;

		case Constants.CODEVOTE:
			handleVote(packet);
			break;

		case Constants.CODESOUND:
			break;

		case Constants.CODEGAMESTATE:
			handleGameState(packet);
			break;

		case Constants.PING:
			handlePing(packet);
			break;

		default:
			// Unknown packet
			break;
		}
	}

	/**
	 * Handle activity based on gamestate
	 * 
	 * @param packet
	 */
	private void handleGameState(JSONObject packet) {
		try {
			GameSurface.ActivityHandler(packet.getInt("State"));
		} catch (JSONException e) {
			e.printStackTrace();
		}
		
	}

	/**
	 * Set health from received JSONObject
	 * 
	 * @param packet received JSONOBject
	 */
	public void handleHealth(JSONObject packet) {
		try {
			GameSurface.health = packet.getInt("Health");
		} catch (JSONException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Send PONG on 'Alive? PING' request
	 * 
	 * @param packet
	 *            - received JSONOBject
	 */
	private void handlePing(JSONObject packet) {
		try {
			if (packet.getBoolean("Ping")) {
				JSONObject jOb = new JSONObject();
				jOb.put("Code", Constants.PONG);
				jOb.put("Pong", true);
				Connection.getInstance().send(jOb);
			}
		} catch (JSONException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Set powerUp from received JSONObject
	 * 
	 * @param packet received JSONOBject
	 */
	private void handlePowerUp(JSONObject packet) {
		try {
			GameSurface.handlePowerUp(packet.getInt("Powerup"), true);
		} catch (JSONException e) {
			e.printStackTrace();
		}

	}

	/**
	 * Set votes from received JSONObject
	 * 
	 * @param packet received JSONOBject
	 */
	private void handleVote(JSONObject packet) {
		try {
			int[] votes = new int[] { packet.getInt("Game1"),
					packet.getInt("Game2"), packet.getInt("Game3") };
			GameSurface.setVotingScreen(votes);
		} catch (JSONException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * Send message to game to disconnect player.
	 */
	public void disconnect() {
		JSONObject jOb = new JSONObject();
		try {
			jOb.put("Code", Constants.CODEDISCONNECT);
			Connection.getInstance().send(jOb);

		} catch (JSONException e) {
			e.printStackTrace();
		}
		Connection.getInstance().send(jOb);
		try {
			socket.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}
